Set up for analysis
Install and load R packages and setup directories
The first step is to install and load all necessary R packages
required for the pipeline, as well as the FASTQC and BLAST+ command line
software.
#Set required packages
.cran_packages <- c(
"devtools",
"ggplot2",
"gridExtra",
"data.table",
"tidyverse",
"stringdist",
"patchwork",
"vegan",
"seqinr",
"patchwork",
"stringi",
"magrittr",
"targets",
"tarchetypes",
"zen4R",
"fs"
)
.bioc_packages <- c(
"phyloseq",
"DECIPHER",
"Biostrings",
"ShortRead",
"ggtree",
"savR",
"dada2",
"ngsReports"
)
.inst <- .cran_packages %in% installed.packages()
if(any(!.inst)) {
install.packages(.cran_packages[!.inst])
}
.inst <- .bioc_packages %in% installed.packages()
if(any(!.inst)) {
if (!requireNamespace("BiocManager", quietly = TRUE)){
install.packages("BiocManager")
}
BiocManager::install(.bioc_packages[!.inst], ask = F)
}
#Load all published packages
sapply(c(.cran_packages,.bioc_packages), require, character.only = TRUE)
# Install and load github packages
devtools::install_github("alexpiper/seqateurs", dependencies = TRUE)
library(seqateurs)
devtools::install_github("alexpiper/taxreturn", dependencies = TRUE)
library(taxreturn)
devtools::install_github("alexpiper/afdscraper", dependencies = TRUE)
library(afdscraper)
devtools::install_github("mikemc/speedyseq", dependencies = TRUE)
library(speedyseq)
#Install bbmap if its not in $path or in bin folder
if(Sys.which("bbduk") == "" & !file.exists("bin/bbmap/bbduk.sh")){
seqateurs::bbmap_install(dest_dir = "bin")
}
#Install fastqc if its not in $path or in bin folder
if(Sys.which("fastqc") == "" & !file.exists("bin/FastQC/fastqc")){
seqateurs::fastqc_install(dest_dir = "bin")
}
#Install BLAST if its not in $path or in bin folder
if(Sys.which("blastn") == "" & (length(fs::dir_ls("bin", glob="*blastn.exe",recurse = TRUE)) ==0)){
taxreturn::blast_install(dest_dir = "bin")
}
source("R/dependencies.R")
source("R/functions.R")
source("R/themes.R")
Create directory structure
This step creates the required directory structure for the pipeline
to run.
# Create directories
if(!dir.exists("data")){dir.create("data", recursive = TRUE)}
if(!dir.exists("reference")){dir.create("reference", recursive = TRUE)}
if(!dir.exists("output/logs")){dir.create("output/logs", recursive = TRUE)}
if(!dir.exists("output/results")){dir.create("output/results", recursive = TRUE)}
if(!dir.exists("output/rds")){dir.create("output/rds", recursive = TRUE)}
if(!dir.exists("sample_data")){dir.create("sample_data", recursive = TRUE)}
if(!dir.exists("output/results/final")) {dir.create("output/results/final", recursive = TRUE)}
if(!dir.exists("output/results/unfiltered")) {dir.create("output/results/unfiltered", recursive = TRUE)}
if(!dir.exists("output/results/filtered")) {dir.create("output/results/filtered", recursive = TRUE)}
Fetch sequencing reads
Some test sequencing reads have been hosted on Zenodo for this
workshop. The below code will download these and put them inside the
data folder.
# Create directory for data
if(!dir.exists("data/K77JP")) {dir.create("data/K77JP", recursive = TRUE)}
if(!dir.exists("data/K77JP/InterOp")) {dir.create("data/K77JP/InterOp", recursive = TRUE)}
# Download files from zenodo
download_zenodo(
doi = "10.5281/zenodo.7112162",
path = "data/K77JP"
)
# Move the interop files to the interop folder
fs::dir_ls(path="data/K77JP", glob="*.bin") %>%
purrr::map(function(x){
fs::file_copy(path = x, new_path = x %>% str_replace("data/K77JP", "data/K77JP/InterOp"))
file.remove(x)
})
The directory structure should now look something like this:
root/
├── data/
│ ├── K77JP/
│ ├── R1.fastq.gz
│ ├── R2.fastq.gz
│ ├── runInfo.xml
│ ├── runParameters.xml
│ ├── SampleSheet.csv
│ └── InterOp/
├── sample_data/
├── reference
├── bin
├── output/
└── doc/
Create sample sheet
In order to track samples and relevant QC statistics throughout the
metabarcoding pipeline, we will first create a new samplesheet from our
input samplesheets. This function requires both the SampleSheet.csv used
for the sequencing run, and the runParameters.xml, both of which should
have been automatically obtained from the demultiplexed sequencing run
folder in the bash step above
# Find all flowcell subdirectories within the data directory
runs <- dir("data/")
SampleSheet <- list.files(paste0("data/", runs), pattern= "SampleSheet", full.names = TRUE)
runParameters <- list.files(paste0("data/", runs), pattern= "[Rr]unParameters.xml", full.names = TRUE)
# Create samplesheet containing samples and run parameters for all runs
samdf <- create_samplesheet(SampleSheet = SampleSheet, runParameters = runParameters, template = "V4") %>%
distinct()
# Check if sampleids contain fcid, if not, attatch these
samdf <- samdf %>%
mutate(sample_id = case_when(
!str_detect(sample_id, fcid) ~ paste0(fcid,"_",sample_id),
TRUE ~ sample_id
))
# Get a list of the fastq files
fastqFs <- purrr::map(list.dirs("data", recursive=FALSE),
list.files, pattern="_R1_", full.names = TRUE) %>%
unlist() %>%
str_remove(pattern = "^(.*)\\/") %>%
str_remove(pattern = "(?:.(?!_S))+$")
fastqFs <- fastqFs[!str_detect(fastqFs, "Undetermined")]
# Find those that are missing in th sample sheet
if (length(setdiff(fastqFs, samdf$sample_id)) > 0) {warning("The fastq file/s: ", setdiff(fastqFs, samdf$sample_id), " are not in the sample sheet") }
# Find those samples in the samplesheet that don't have fastq files
if (length(setdiff(samdf$sample_id, fastqFs)) > 0) {
warning(paste0("The fastq file: ",
setdiff(samdf$sample_id, fastqFs),
" is missing, dropping from samplesheet \n"))
samdf <- samdf %>%
filter(!sample_id %in% setdiff(samdf$sample_id, fastqFs))
}
# Add PCR primers to sample sheet
samdf <- samdf %>%
mutate(
pcr_primers = "fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4",
for_primer_seq = "GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC",
rev_primer_seq = "GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC"
)
#Write out sample CSV for use in pipeline
dir.create("sample_data")
write_csv(samdf, "sample_data/Sample_info.csv")
The resulting sample data file should look like this:
|
sample_id
|
sample_name
|
extraction_rep
|
amp_rep
|
client_name
|
experiment_name
|
sample_type
|
collection_method
|
collection_location
|
lat_lon
|
environment
|
collection_date
|
operator_name
|
description
|
assay
|
extraction_method
|
amp_method
|
target_gene
|
pcr_primers
|
for_primer_seq
|
rev_primer_seq
|
index_plate
|
index_well
|
i7_index_id
|
i7_index
|
i5_index_id
|
i5_index
|
seq_platform
|
fcid
|
for_read_length
|
rev_read_length
|
seq_run_id
|
seq_id
|
seq_date
|
analysis_method
|
notes
|
|
K77JP_Trap7
|
Trap7
|
NA
|
NA
|
Pathogens
|
K739J_tephritid_metabarcoding
|
NA
|
NA
|
NA
|
NA
|
NA
|
NA
|
Alexander Piper
|
NA
|
Metabarcoding
|
NA
|
NA
|
NA
|
fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4
|
GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC
|
GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC
|
1
|
B1
|
AVR_DUI_i7_002
|
ACGGAACA
|
AVR_DUI_i5_002
|
CGCTCTAT
|
NA
|
K77JP
|
251
|
251
|
220527_M01054_0780_000000000-K77JP
|
M01054
|
2022-05-27
|
NA
|
NA
|
|
K77JP_Trap6
|
Trap6
|
NA
|
NA
|
Pathogens
|
K739J_tephritid_metabarcoding
|
NA
|
NA
|
NA
|
NA
|
NA
|
NA
|
Alexander Piper
|
NA
|
Metabarcoding
|
NA
|
NA
|
NA
|
fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4
|
GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC
|
GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC
|
1
|
C1
|
AVR_DUI_i7_003
|
CTTAGGAC
|
AVR_DUI_i5_003
|
TGGTAGCT
|
NA
|
K77JP
|
251
|
251
|
220527_M01054_0780_000000000-K77JP
|
M01054
|
2022-05-27
|
NA
|
NA
|
|
K77JP_Trap1
|
Trap1
|
NA
|
NA
|
Pathogens
|
K739J_tephritid_metabarcoding
|
NA
|
NA
|
NA
|
NA
|
NA
|
NA
|
Alexander Piper
|
NA
|
Metabarcoding
|
NA
|
NA
|
NA
|
fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4
|
GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC
|
GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC
|
1
|
H1
|
AVR_DUI_i7_008
|
ACTCGTTG
|
AVR_DUI_i5_008
|
CACCACTA
|
NA
|
K77JP
|
251
|
251
|
220527_M01054_0780_000000000-K77JP
|
M01054
|
2022-05-27
|
NA
|
NA
|
|
K77JP_Trap20
|
Trap20
|
NA
|
NA
|
Pathogens
|
K739J_tephritid_metabarcoding
|
NA
|
NA
|
NA
|
NA
|
NA
|
NA
|
Alexander Piper
|
NA
|
Metabarcoding
|
NA
|
NA
|
NA
|
fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4
|
GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC
|
GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC
|
1
|
E3
|
AVR_DUI_i7_021
|
GCTGACTA
|
AVR_DUI_i5_021
|
CTTAGGAC
|
NA
|
K77JP
|
251
|
251
|
220527_M01054_0780_000000000-K77JP
|
M01054
|
2022-05-27
|
NA
|
NA
|
|
K77JP_Trap19
|
Trap19
|
NA
|
NA
|
Pathogens
|
K739J_tephritid_metabarcoding
|
NA
|
NA
|
NA
|
NA
|
NA
|
NA
|
Alexander Piper
|
NA
|
Metabarcoding
|
NA
|
NA
|
NA
|
fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4
|
GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC
|
GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC
|
1
|
F3
|
AVR_DUI_i7_022
|
CAGGAGAT
|
AVR_DUI_i5_022
|
ACGGAACA
|
NA
|
K77JP
|
251
|
251
|
220527_M01054_0780_000000000-K77JP
|
M01054
|
2022-05-27
|
NA
|
NA
|
Create parameters file
The pipeline also requires a locus parameters file which lists
important information about the target barcodes, as well as the PHMM
model used to clean the loci, as well as the different reference
databases that are to be used for taxonomic assignment.
If multiple reference databases are to be used iteratively for
assignment, they should be split with a semicolon, and in the order they
are to be used for assignment.
The below code generates the loci_params.csv file, however this can
also be done in excel.
params <- tibble(
pcr_primers = c("fwhF2-fwhR2nDac", "EIF3LminiF4-EIF3lminiR4"),
target_gene=c("COI", "EIF3L"),
phmm = c("reference/phmm/Bactrocera_COI.rds", "reference/phmm/Bactrocera_EIF3L.rds"),
ref_db = c("reference/COI_internal_idtaxa.rds;reference/COI_idtaxa.rds","reference/EIF3L_internal_idtaxa.rds;reference/EIF3L_idtaxa.rds"),
blast_db = c("reference/COI_internal.fa.gz;reference/COI_hierarchial.fa.gz", "reference/EIF3L_internal.fa.gz;reference/EIF3L_hierarchial.fa.gz"),
exp_length = c(205, 217),
genetic_code = c("SGC4", "SGC0"),
coding = c(TRUE, TRUE)
)
# Write out the parameters file
write_csv(params, "sample_data/loci_params.csv")
The resulting table should look like this:
|
pcr_primers
|
target_gene
|
phmm
|
ref_db
|
blast_db
|
exp_length
|
genetic_code
|
coding
|
|
fwhF2-fwhR2nDac
|
COI
|
reference/phmm/Bactrocera_COI.rds
|
reference/COI_internal_idtaxa.rds;reference/COI_idtaxa.rds
|
reference/COI_internal.fa.gz;reference/COI_hierarchial.fa.gz
|
205
|
SGC4
|
TRUE
|
|
EIF3LminiF4-EIF3lminiR4
|
EIF3L
|
reference/phmm/Bactrocera_EIF3L.rds
|
reference/EIF3L_internal_idtaxa.rds;reference/EIF3L_idtaxa.rds
|
reference/EIF3L_internal.fa.gz;reference/EIF3L_hierarchial.fa.gz
|
217
|
SGC0
|
TRUE
|
Analyse the results
The outputs of the pipeline are an ASV table, taxonomy table, and
sample data, as well as a phyloseq object containing these 3 components.
these are located in the following directories
- output/results/final/seqtab.csv
- output/results/final/taxtab.csv
- output/results/final/samdf.csv
- output/rds/ps_filtered.rds
Read in data
Here we read in the phyloseq object containing the ASV table,
taxonomy table, and sample data
ps <- readRDS("output/rds/ps_filtered.rds")
# Turn the phyloseq object into an R data frame, and apply 0.01% minimum abundance threshold
summary_dat <- ps %>%
speedyseq::tax_glom(taxrank = "Species") %>% # Merges all ASVs assigned to the same species
speedyseq::psmelt() %>% # Transforms to data frame
filter(Abundance > 0 ) %>%
dplyr::select(OTU, Sample, Abundance,pcr_primers, sample_id, sample_name, environment, collection_location, collection_date, fcid, rank_names(ps)) %>%
group_by(sample_id, pcr_primers) %>%
mutate_at(vars(Abundance), ~ . / sum(.) ) %>%
filter(Abundance > 1e-4) # Remove all under 0.01% abundance
Heatmaps
gg.heatmap <- summary_dat%>%
mutate(sample_name = factor(sample_name,levels=c("Trap1", "Trap6", "Trap7", "Trap19", "Trap20"))) %>%
ggplot(aes(x=sample_name, y=Species, fill=Abundance)) +
geom_tile() +
scale_fill_viridis_c(labels = scales::percent, na.value = NA, alpha=0.9) +
scale_y_discrete(limits=rev)+
base_theme+
theme(legend.position = "right",
axis.text.y = element_text(face="italic"),
axis.title.y = element_blank())+
labs(x="Sample",
y="Taxon",
fill="Relative abundance") +
facet_grid(~pcr_primers, drop=TRUE)
ggplotly(gg.heatmap)
Barplots
gg.barplot <- summary_dat%>%
mutate(sample_name = factor(sample_name,levels=c("Trap1", "Trap6", "Trap7", "Trap19", "Trap20"))) %>%
ggplot(aes(x=sample_name, y=Abundance, fill=Species)) +
geom_col(colour="black") +
#scale_fill_viridis_c(labels = scales::percent, na.value = NA, alpha=0.9) +
#scale_y_discrete(limits=rev)+
base_theme+
theme(legend.position = "none",
axis.text.y = element_text(face="italic"),
axis.title.y = element_blank())+
labs(x="Sample",
y="Taxon",
fill="Relative abundance") +
facet_grid(~pcr_primers, drop=TRUE)
ggplotly(gg.barplot)
LS0tDQp0aXRsZTogIlRlcGhyaXRpZCBtZXRhYmFyY29kaW5nIHdvcmtzaG9wIg0Kc3VidGl0bGU6ICJCaW9pbmZvcm1hdGljIGFuYWx5c2lzIg0KYXV0aG9yOiAiQWxleGFuZGVyIE0uIFBpcGVyIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBoaWdobGlnaHRlcjogbnVsbA0KICAgIHRoZW1lOiAiZmxhdGx5Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZCAgICANCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIEtuaXRyIGdsb2JhbCBzZXR1cCAtIGNoYW5nZSBldmFsIHRvIHRydWUgdG8gcnVuIGNvZGUNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0YXJnZXRzKQ0KbGlicmFyeSh0YXJjaGV0eXBlcykNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZmlnLnNob3cgPSAiaG9sZCIsIGZpZy5rZWVwID0gImFsbCIpDQpvcHRzX2NodW5rJHNldChkZXYgPSAncG5nJykNCnNvdXJjZSgiUi90aGVtZXMuUiIpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhpcyB3b3Jrc2hvcCB3ZSB3aWxsIGJlIGFuYWx5c2luZyA1IGV4YW1wbGUgc2FtcGxlcyBmcm9tIGEgcHJldmlvdXMgYmF0Y2ggb2Ygc2FtcGxlcy4gDQoNClRoZSBtb3JwaG9sb2dpY2FsIGlkZW50aWZpY2F0aW9uIG9mIHRoZXNlIHNhbXBsZXMgcmVjb3JkZWQgdGhlIGZvbGxvd2luZyBzcGVjaWVzOg0KDQpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQ0KdHJhcF9jYXRjaGVzIDwtIHRpYmJsZTo6dHJpYmJsZSgNCiAgflNwZWNpZXMsIH5UcmFwMSwgflRyYXA2LCB+VHJhcDcsIH5UcmFwMTksIH5UcmFwMjAsDQogIkIuIGFlcm9naW5vc2EiLCAwLDAsMCwxLDAsDQogIkIuIGFseXhpYWUiLCA2LDEsMTUsMTMsMCwNCiAiQi4gYnJldmlhY3VsZXVzIiw5LDUsMTgsOCwwLA0KICJCLiBicnlvbmlhZSIsMSw2NSwzNSwxNSw3LA0KICJCLiBlcnViZXNjZW50aXMiLDUyLDAsMCwwLDAsDQogIkIuIGZyYXVlbmZlbGRpIiwxNTgsMTY0LDE5Miw3ODMsMCwNCiAiQi4gbmVvaHVtZXJhbGlzIiwxNywzNCwyMCwxNDgsMzAsDQogIkIuIHBlbmluc3VsYXJpcyIsMiwxLDEsMCwwLA0KICJCLiB0ZW51aWZhc2NpYSIsMSwwLDAsMCwwLA0KICJCLiB0cnlvbmkiLDEyLDM2LDMxLDk0LDgyNiwNCiAiRC4gYXhhbnVzIiwzLDAsMCwwLDAsDQogIlouIGNob3Jpc3R1cyIsMCwwLDAsMSwwLA0KICJaLiBzdHJpZ2lmaW5pcyIsMSw1LDIsMCwwDQopDQp0cmFwX2NhdGNoZXMlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX2NsYXNzaWMoImhvdmVyIiwgZnVsbF93aWR0aCA9IFQpDQpgYGANCg0KQW5kIHZpc3VhbGx5LCB0aGUgdHJhcCBjb21wb3NpdGlvbnMgaWRlbnRpZmllZCB0aHJvdWdoIG1vcnBob2xvZ3kgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9DQpwbG90IDwtIHRyYXBfY2F0Y2hlcyAlPiUNCiAgcGl2b3RfbG9uZ2VyKC1TcGVjaWVzLA0KICAgICAgICAgICAgICAgbmFtZXNfdG89InNhbXBsZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG89InNwZWNpbWVucyIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBzcGVjaW1lbnMsIGZpbGw9U3BlY2llcykpICsgDQogIGdlb21fY29sKCkgKw0KICAjc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpKw0KICBiYXNlX3RoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICBsYWJzKHggPSAiVHJhcCBzYW1wbGUiLA0KICAgICAgIHkgPSAiTnVtYmVyIG9mIG1vcnBob2xvZ2ljYWxseSBpZGVudGlmaWVkIHNwZWNpbWVucyIpIA0KDQpnZ3Bsb3RseShwbG90KQ0KYGBgDQoNCiMgU2V0IHVwIGZvciBhbmFseXNpcw0KDQojIyBJbnN0YWxsIGFuZCBsb2FkIFIgcGFja2FnZXMgYW5kIHNldHVwIGRpcmVjdG9yaWVzDQoNClRoZSBmaXJzdCBzdGVwIGlzIHRvIGluc3RhbGwgYW5kIGxvYWQgYWxsIG5lY2Vzc2FyeSBSIHBhY2thZ2VzIHJlcXVpcmVkIGZvciB0aGUgcGlwZWxpbmUsIGFzIHdlbGwgYXMgdGhlIEZBU1RRQyBhbmQgQkxBU1QrIGNvbW1hbmQgbGluZSBzb2Z0d2FyZS4NCg0KYGBge3IgaW5zdGFsbCBhbmQgbG9hZCwgZXZhbD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgcmVzdWx0cz1GQUxTRX0gDQojU2V0IHJlcXVpcmVkIHBhY2thZ2VzDQouY3Jhbl9wYWNrYWdlcyA8LSBjKA0KICAiZGV2dG9vbHMiLA0KICAiZ2dwbG90MiIsDQogICJncmlkRXh0cmEiLA0KICAiZGF0YS50YWJsZSIsDQogICJ0aWR5dmVyc2UiLCANCiAgInN0cmluZ2Rpc3QiLA0KICAicGF0Y2h3b3JrIiwNCiAgInZlZ2FuIiwNCiAgInNlcWluciIsDQogICJwYXRjaHdvcmsiLA0KICAic3RyaW5naSIsDQogICJtYWdyaXR0ciIsDQogICJ0YXJnZXRzIiwNCiAgInRhcmNoZXR5cGVzIiwNCiAgInplbjRSIiwNCiAgImZzIg0KICApDQoNCi5iaW9jX3BhY2thZ2VzIDwtIGMoDQogICJwaHlsb3NlcSIsDQogICJERUNJUEhFUiIsDQogICJCaW9zdHJpbmdzIiwNCiAgIlNob3J0UmVhZCIsDQogICJnZ3RyZWUiLA0KICAic2F2UiIsDQogICJkYWRhMiIsDQogICJuZ3NSZXBvcnRzIg0KICApDQoNCi5pbnN0IDwtIC5jcmFuX3BhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkNCmlmKGFueSghLmluc3QpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKC5jcmFuX3BhY2thZ2VzWyEuaW5zdF0pDQp9DQouaW5zdCA8LSAuYmlvY19wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpDQppZihhbnkoIS5pbnN0KSkgew0KICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpDQogIH0NCiAgQmlvY01hbmFnZXI6Omluc3RhbGwoLmJpb2NfcGFja2FnZXNbIS5pbnN0XSwgYXNrID0gRikNCn0NCg0KI0xvYWQgYWxsIHB1Ymxpc2hlZCBwYWNrYWdlcw0Kc2FwcGx5KGMoLmNyYW5fcGFja2FnZXMsLmJpb2NfcGFja2FnZXMpLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQoNCiMgSW5zdGFsbCBhbmQgbG9hZCBnaXRodWIgcGFja2FnZXMNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWxleHBpcGVyL3NlcWF0ZXVycyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQpsaWJyYXJ5KHNlcWF0ZXVycykNCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhbGV4cGlwZXIvdGF4cmV0dXJuIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkodGF4cmV0dXJuKQ0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImFsZXhwaXBlci9hZmRzY3JhcGVyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkoYWZkc2NyYXBlcikNCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJtaWtlbWMvc3BlZWR5c2VxIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkoc3BlZWR5c2VxKQ0KDQojSW5zdGFsbCBiYm1hcCBpZiBpdHMgbm90IGluICRwYXRoIG9yIGluIGJpbiBmb2xkZXINCmlmKFN5cy53aGljaCgiYmJkdWsiKSA9PSAiIiAmICFmaWxlLmV4aXN0cygiYmluL2JibWFwL2JiZHVrLnNoIikpew0KICBzZXFhdGV1cnM6OmJibWFwX2luc3RhbGwoZGVzdF9kaXIgPSAiYmluIikNCn0NCg0KI0luc3RhbGwgZmFzdHFjIGlmIGl0cyBub3QgaW4gJHBhdGggb3IgaW4gYmluIGZvbGRlcg0KaWYoU3lzLndoaWNoKCJmYXN0cWMiKSA9PSAiIiAmICFmaWxlLmV4aXN0cygiYmluL0Zhc3RRQy9mYXN0cWMiKSl7DQogIHNlcWF0ZXVyczo6ZmFzdHFjX2luc3RhbGwoZGVzdF9kaXIgPSAiYmluIikNCn0NCg0KI0luc3RhbGwgQkxBU1QgaWYgaXRzIG5vdCBpbiAkcGF0aCBvciBpbiBiaW4gZm9sZGVyDQppZihTeXMud2hpY2goImJsYXN0biIpID09ICIiICYgKGxlbmd0aChmczo6ZGlyX2xzKCJiaW4iLCBnbG9iPSIqYmxhc3RuLmV4ZSIscmVjdXJzZSA9IFRSVUUpKSA9PTApKXsNCiAgdGF4cmV0dXJuOjpibGFzdF9pbnN0YWxsKGRlc3RfZGlyID0gImJpbiIpDQp9DQoNCnNvdXJjZSgiUi9kZXBlbmRlbmNpZXMuUiIpDQpzb3VyY2UoIlIvZnVuY3Rpb25zLlIiKQ0Kc291cmNlKCJSL3RoZW1lcy5SIikNCmBgYA0KDQojIyBDcmVhdGUgZGlyZWN0b3J5IHN0cnVjdHVyZQ0KDQpUaGlzIHN0ZXAgY3JlYXRlcyB0aGUgcmVxdWlyZWQgZGlyZWN0b3J5IHN0cnVjdHVyZSBmb3IgdGhlIHBpcGVsaW5lIHRvIHJ1bi4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZGlyZWN0b3JpZXMNCmlmKCFkaXIuZXhpc3RzKCJkYXRhIikpe2Rpci5jcmVhdGUoImRhdGEiLCByZWN1cnNpdmUgPSBUUlVFKX0NCmlmKCFkaXIuZXhpc3RzKCJyZWZlcmVuY2UiKSl7ZGlyLmNyZWF0ZSgicmVmZXJlbmNlIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L2xvZ3MiKSl7ZGlyLmNyZWF0ZSgib3V0cHV0L2xvZ3MiLCByZWN1cnNpdmUgPSBUUlVFKX0NCmlmKCFkaXIuZXhpc3RzKCJvdXRwdXQvcmVzdWx0cyIpKXtkaXIuY3JlYXRlKCJvdXRwdXQvcmVzdWx0cyIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoIm91dHB1dC9yZHMiKSl7ZGlyLmNyZWF0ZSgib3V0cHV0L3JkcyIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoInNhbXBsZV9kYXRhIikpe2Rpci5jcmVhdGUoInNhbXBsZV9kYXRhIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L3Jlc3VsdHMvZmluYWwiKSkge2Rpci5jcmVhdGUoIm91dHB1dC9yZXN1bHRzL2ZpbmFsIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L3Jlc3VsdHMvdW5maWx0ZXJlZCIpKSB7ZGlyLmNyZWF0ZSgib3V0cHV0L3Jlc3VsdHMvdW5maWx0ZXJlZCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoIm91dHB1dC9yZXN1bHRzL2ZpbHRlcmVkIikpIHtkaXIuY3JlYXRlKCJvdXRwdXQvcmVzdWx0cy9maWx0ZXJlZCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KYGBgDQoNCiMjIEZldGNoIHNlcXVlbmNpbmcgcmVhZHMNCg0KU29tZSB0ZXN0IHNlcXVlbmNpbmcgcmVhZHMgaGF2ZSBiZWVuIGhvc3RlZCBvbiBaZW5vZG8gZm9yIHRoaXMgd29ya3Nob3AuIFRoZSBiZWxvdyBjb2RlIHdpbGwgZG93bmxvYWQgdGhlc2UgYW5kIHB1dCB0aGVtIGluc2lkZSB0aGUgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZGlyZWN0b3J5IGZvciBkYXRhDQppZighZGlyLmV4aXN0cygiZGF0YS9LNzdKUCIpKSB7ZGlyLmNyZWF0ZSgiZGF0YS9LNzdKUCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoImRhdGEvSzc3SlAvSW50ZXJPcCIpKSB7ZGlyLmNyZWF0ZSgiZGF0YS9LNzdKUC9JbnRlck9wIiwgcmVjdXJzaXZlID0gVFJVRSl9DQoNCiMgRG93bmxvYWQgZmlsZXMgZnJvbSB6ZW5vZG8NCmRvd25sb2FkX3plbm9kbygNCmRvaSA9ICIxMC41MjgxL3plbm9kby43MTEyMTYyIiwNCnBhdGggPSAiZGF0YS9LNzdKUCINCikNCg0KIyBNb3ZlIHRoZSBpbnRlcm9wIGZpbGVzIHRvIHRoZSBpbnRlcm9wIGZvbGRlcg0KZnM6OmRpcl9scyhwYXRoPSJkYXRhL0s3N0pQIiwgZ2xvYj0iKi5iaW4iKSAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBmczo6ZmlsZV9jb3B5KHBhdGggPSB4LCBuZXdfcGF0aCA9IHggJT4lIHN0cl9yZXBsYWNlKCJkYXRhL0s3N0pQIiwgImRhdGEvSzc3SlAvSW50ZXJPcCIpKQ0KICAgIGZpbGUucmVtb3ZlKHgpDQogIH0pDQpgYGANCg0KVGhlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgc2hvdWxkIG5vdyBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCiAgICByb290Lw0KICAgIOKUnOKUgOKUgCBkYXRhLw0KICAgIOKUgiAgIOKUnOKUgOKUgCBLNzdKUC8NCiAgICDilIIgICAgIOKUnOKUgOKUgCBSMS5mYXN0cS5neg0KICAgIOKUgiAgICAg4pSc4pSA4pSAIFIyLmZhc3RxLmd6DQogICAg4pSCICAgICDilJzilIDilIAgcnVuSW5mby54bWwNCiAgICDilIIgICAgIOKUnOKUgOKUgCBydW5QYXJhbWV0ZXJzLnhtbA0KICAgIOKUgiAgICAg4pSc4pSA4pSAIFNhbXBsZVNoZWV0LmNzdg0KICAgIOKUgiAgICAg4pSU4pSA4pSAIEludGVyT3AvDQogICAg4pSc4pSA4pSAIHNhbXBsZV9kYXRhLw0KICAgIOKUnOKUgOKUgCByZWZlcmVuY2UNCiAgICDilJzilIDilIAgYmluDQogICAg4pSc4pSA4pSAIG91dHB1dC8NCiAgICDilJTilIDilIAgZG9jLw0KDQoNCiMjIENyZWF0ZSBzYW1wbGUgc2hlZXQgDQoNCkluIG9yZGVyIHRvIHRyYWNrIHNhbXBsZXMgYW5kIHJlbGV2YW50IFFDIHN0YXRpc3RpY3MgdGhyb3VnaG91dCB0aGUgbWV0YWJhcmNvZGluZyBwaXBlbGluZSwgd2Ugd2lsbCBmaXJzdCBjcmVhdGUgYSBuZXcgc2FtcGxlc2hlZXQgZnJvbSBvdXIgaW5wdXQgc2FtcGxlc2hlZXRzLiBUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIGJvdGggdGhlIFNhbXBsZVNoZWV0LmNzdiB1c2VkIGZvciB0aGUgc2VxdWVuY2luZyBydW4sIGFuZCB0aGUgcnVuUGFyYW1ldGVycy54bWwsIGJvdGggb2Ygd2hpY2ggc2hvdWxkIGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IG9idGFpbmVkIGZyb20gdGhlIGRlbXVsdGlwbGV4ZWQgc2VxdWVuY2luZyBydW4gZm9sZGVyIGluIHRoZSBiYXNoIHN0ZXAgYWJvdmUNCg0KYGBge3IgY3JlYXRlIHNhbXBsZXNoZWV0LCBldmFsPVRSVUV9DQojIEZpbmQgYWxsIGZsb3djZWxsIHN1YmRpcmVjdG9yaWVzIHdpdGhpbiB0aGUgZGF0YSBkaXJlY3RvcnkNCnJ1bnMgPC0gZGlyKCJkYXRhLyIpDQpTYW1wbGVTaGVldCA8LSBsaXN0LmZpbGVzKHBhc3RlMCgiZGF0YS8iLCBydW5zKSwgcGF0dGVybj0gIlNhbXBsZVNoZWV0IiwgZnVsbC5uYW1lcyA9IFRSVUUpDQpydW5QYXJhbWV0ZXJzIDwtIGxpc3QuZmlsZXMocGFzdGUwKCJkYXRhLyIsIHJ1bnMpLCBwYXR0ZXJuPSAiW1JyXXVuUGFyYW1ldGVycy54bWwiLCBmdWxsLm5hbWVzID0gVFJVRSkNCg0KIyBDcmVhdGUgc2FtcGxlc2hlZXQgY29udGFpbmluZyBzYW1wbGVzIGFuZCBydW4gcGFyYW1ldGVycyBmb3IgYWxsIHJ1bnMNCnNhbWRmIDwtIGNyZWF0ZV9zYW1wbGVzaGVldChTYW1wbGVTaGVldCA9IFNhbXBsZVNoZWV0LCBydW5QYXJhbWV0ZXJzID0gcnVuUGFyYW1ldGVycywgdGVtcGxhdGUgPSAiVjQiKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojIENoZWNrIGlmIHNhbXBsZWlkcyBjb250YWluIGZjaWQsIGlmIG5vdCwgYXR0YXRjaCB0aGVzZQ0Kc2FtZGYgPC0gc2FtZGYgJT4lDQogIG11dGF0ZShzYW1wbGVfaWQgPSBjYXNlX3doZW4oDQogICAgIXN0cl9kZXRlY3Qoc2FtcGxlX2lkLCBmY2lkKSB+IHBhc3RlMChmY2lkLCJfIixzYW1wbGVfaWQpLA0KICAgIFRSVUUgfiBzYW1wbGVfaWQNCiAgKSkNCg0KIyBHZXQgYSBsaXN0IG9mIHRoZSBmYXN0cSBmaWxlcw0KZmFzdHFGcyA8LSBwdXJycjo6bWFwKGxpc3QuZGlycygiZGF0YSIsIHJlY3Vyc2l2ZT1GQUxTRSksDQogICAgICAgICAgICAgICAgICAgICAgbGlzdC5maWxlcywgcGF0dGVybj0iX1IxXyIsIGZ1bGwubmFtZXMgPSBUUlVFKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIHN0cl9yZW1vdmUocGF0dGVybiA9ICJeKC4qKVxcLyIpICU+JQ0KICBzdHJfcmVtb3ZlKHBhdHRlcm4gPSAiKD86Lig/IV9TKSkrJCIpDQpmYXN0cUZzIDwtIGZhc3RxRnNbIXN0cl9kZXRlY3QoZmFzdHFGcywgIlVuZGV0ZXJtaW5lZCIpXQ0KDQojIEZpbmQgdGhvc2UgdGhhdCBhcmUgbWlzc2luZyBpbiB0aCBzYW1wbGUgc2hlZXQNCmlmIChsZW5ndGgoc2V0ZGlmZihmYXN0cUZzLCBzYW1kZiRzYW1wbGVfaWQpKSA+IDApIHt3YXJuaW5nKCJUaGUgZmFzdHEgZmlsZS9zOiAiLCBzZXRkaWZmKGZhc3RxRnMsIHNhbWRmJHNhbXBsZV9pZCksICIgYXJlIG5vdCBpbiB0aGUgc2FtcGxlIHNoZWV0IikgfQ0KDQojIEZpbmQgdGhvc2Ugc2FtcGxlcyBpbiB0aGUgc2FtcGxlc2hlZXQgdGhhdCBkb24ndCBoYXZlIGZhc3RxIGZpbGVzDQppZiAobGVuZ3RoKHNldGRpZmYoc2FtZGYkc2FtcGxlX2lkLCBmYXN0cUZzKSkgPiAwKSB7DQogIHdhcm5pbmcocGFzdGUwKCJUaGUgZmFzdHEgZmlsZTogIiwNCiAgICAgICAgICAgICAgICAgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpLA0KICAgICAgICAgICAgICAgICAiIGlzIG1pc3NpbmcsIGRyb3BwaW5nIGZyb20gc2FtcGxlc2hlZXQgXG4iKSkgDQogIHNhbWRmIDwtIHNhbWRmICU+JQ0KICAgIGZpbHRlcighc2FtcGxlX2lkICVpbiUgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpKQ0KfQ0KDQojIEFkZCBQQ1IgcHJpbWVycyB0byBzYW1wbGUgc2hlZXQNCnNhbWRmIDwtIHNhbWRmICU+JQ0KICBtdXRhdGUoDQogICAgcGNyX3ByaW1lcnMgPSAiZndoRjItZndoUjJuRGFjO0VJRjNMbWluaUY0LUVJRjNsbWluaVI0IiwNCiAgICBmb3JfcHJpbWVyX3NlcSA9ICJHR0RBQ1dHR1dUR0FBQ1dHVFdUQVlDQ0hDQztHQVRHQ0dZQ0dUVEFUR0NZR0FUR0MiLA0KICAgIHJldl9wcmltZXJfc2VxID0gIkdUUkFUV0dDSENDSUdDVEFBREFDSEdHO1RUUkFBWUFDVFRDWUFSQVRDUkNDIg0KICAgICkNCg0KI1dyaXRlIG91dCBzYW1wbGUgQ1NWIGZvciB1c2UgaW4gcGlwZWxpbmUNCmRpci5jcmVhdGUoInNhbXBsZV9kYXRhIikNCndyaXRlX2NzdihzYW1kZiwgInNhbXBsZV9kYXRhL1NhbXBsZV9pbmZvLmNzdiIpDQpgYGANCg0KVGhlIHJlc3VsdGluZyBzYW1wbGUgZGF0YSBmaWxlIHNob3VsZCBsb29rIGxpa2UgdGhpczoNCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9DQpzYW1kZiAlPiUNCiAgaGVhZCgpJT4lDQogIGthYmxlKCkgJT4lDQogIGthYmxlX2NsYXNzaWMoImhvdmVyIiwgZnVsbF93aWR0aCA9IFQpDQpgYGANCg0KDQojIyBDcmVhdGUgcGFyYW1ldGVycyBmaWxlDQoNClRoZSBwaXBlbGluZSBhbHNvIHJlcXVpcmVzIGEgbG9jdXMgcGFyYW1ldGVycyBmaWxlIHdoaWNoIGxpc3RzIGltcG9ydGFudCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdGFyZ2V0IGJhcmNvZGVzLCBhcyB3ZWxsIGFzIHRoZSBQSE1NIG1vZGVsIHVzZWQgdG8gY2xlYW4gdGhlIGxvY2ksIGFzIHdlbGwgYXMgdGhlIGRpZmZlcmVudCByZWZlcmVuY2UgZGF0YWJhc2VzIHRoYXQgYXJlIHRvIGJlIHVzZWQgZm9yIHRheG9ub21pYyBhc3NpZ25tZW50Lg0KDQpJZiBtdWx0aXBsZSByZWZlcmVuY2UgZGF0YWJhc2VzIGFyZSB0byBiZSB1c2VkIGl0ZXJhdGl2ZWx5IGZvciBhc3NpZ25tZW50LCB0aGV5IHNob3VsZCBiZSBzcGxpdCB3aXRoIGEgc2VtaWNvbG9uLCBhbmQgaW4gdGhlIG9yZGVyIHRoZXkgYXJlIHRvIGJlIHVzZWQgZm9yIGFzc2lnbm1lbnQuDQoNClRoZSBiZWxvdyBjb2RlIGdlbmVyYXRlcyB0aGUgbG9jaV9wYXJhbXMuY3N2IGZpbGUsIGhvd2V2ZXIgdGhpcyBjYW4gYWxzbyBiZSBkb25lIGluIGV4Y2VsLg0KDQoNCmBgYHtyIENyZWF0ZSBwYXJhbWV0ZXJzIGZpbGUsIGV2YWw9VFJVRX0NCnBhcmFtcyA8LSB0aWJibGUoDQogIHBjcl9wcmltZXJzID0gYygiZndoRjItZndoUjJuRGFjIiwgIkVJRjNMbWluaUY0LUVJRjNsbWluaVI0IiksDQogIHRhcmdldF9nZW5lPWMoIkNPSSIsICJFSUYzTCIpLA0KICBwaG1tID0gYygicmVmZXJlbmNlL3BobW0vQmFjdHJvY2VyYV9DT0kucmRzIiwgInJlZmVyZW5jZS9waG1tL0JhY3Ryb2NlcmFfRUlGM0wucmRzIiksDQogIHJlZl9kYiA9IGMoInJlZmVyZW5jZS9DT0lfaW50ZXJuYWxfaWR0YXhhLnJkcztyZWZlcmVuY2UvQ09JX2lkdGF4YS5yZHMiLCJyZWZlcmVuY2UvRUlGM0xfaW50ZXJuYWxfaWR0YXhhLnJkcztyZWZlcmVuY2UvRUlGM0xfaWR0YXhhLnJkcyIpLA0KICBibGFzdF9kYiA9IGMoInJlZmVyZW5jZS9DT0lfaW50ZXJuYWwuZmEuZ3o7cmVmZXJlbmNlL0NPSV9oaWVyYXJjaGlhbC5mYS5neiIsICJyZWZlcmVuY2UvRUlGM0xfaW50ZXJuYWwuZmEuZ3o7cmVmZXJlbmNlL0VJRjNMX2hpZXJhcmNoaWFsLmZhLmd6IiksDQogIGV4cF9sZW5ndGggPSBjKDIwNSwgMjE3KSwNCiAgZ2VuZXRpY19jb2RlID0gYygiU0dDNCIsICJTR0MwIiksDQogIGNvZGluZyA9IGMoVFJVRSwgVFJVRSkNCikNCg0KIyBXcml0ZSBvdXQgdGhlIHBhcmFtZXRlcnMgZmlsZQ0Kd3JpdGVfY3N2KHBhcmFtcywgInNhbXBsZV9kYXRhL2xvY2lfcGFyYW1zLmNzdiIpDQpgYGANCg0KVGhlIHJlc3VsdGluZyB0YWJsZSBzaG91bGQgbG9vayBsaWtlIHRoaXM6DQpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQ0KcGFyYW1zICU+JQ0KICBrYWJsZSgpICU+JQ0KICBjb2x1bW5fc3BlYygzLCB3aWR0aCA9ICIxY20iKSU+JQ0KICBjb2x1bW5fc3BlYyg0LCB3aWR0aCA9ICIxY20iKSU+JQ0KICBjb2x1bW5fc3BlYyg1LCB3aWR0aCA9ICIxY20iKSAlPiUNCiAga2FibGVfY2xhc3NpYygiaG92ZXIiLCBmdWxsX3dpZHRoID0gVCkNCmBgYA0KDQoNCiMgUnVuIHRoZSBwaXBlbGluZQ0KDQpOb3cgdGhhdCB0aGUgc2FtcGxlIGRhdGEgYW5kIHBhcmFtZXRlcnMgZmlsZSBpcyByZWFkeSwgaXRzIHRpbWUgdG8gcnVuIHRoZSBwaXBlbGluZSENCg0KIyMgVmlzdWFsaXNlIHRoZSBwaXBlbGluZSBzdGVwcw0KDQpgYGB7ciwgZXZhbD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMgVmlzdWFsaXNlIHRoZSBwbGFubmVkIHRhcmdldHMgcGlwZWxpbmUNCnRhcl9nbGltcHNlKCkNCmBgYA0KDQoNCiMjIFJ1biB0aGUgdGFyZ2V0cyBwaXBlbGluZQ0KYGBge3J9DQp0YXJfbWFrZSgpDQpgYGANCg0KDQojIyBDaGVjayBxdWFsaXR5IGNvbnRyb2wgb3V0cHV0cw0KDQpUaGVyZSBhcmUgYSBmZXcgaW1wb3J0YW50IHF1YWxpdHkgY29udHJvbCBwbG90cyB0aGF0IGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IGNyZWF0ZWQgdGhyb3VnaG91dCB0aGUgcGlwZWxpbmUuIFRoZXNlIHNob3VsZCBiZSBjaGVja2VkIHRvIGVuc3VyZSB0aGUgc2VxdWVuY2luZyBydW4gYW5kIGFuYWx5c2lzIGhhcyBydW4gc3VjY2Vzc2Z1bGx5Og0KDQoqIFNlcXVlbmNpbmcgcnVuIHF1YWxpdHkgY2hlY2sNCiAgKyBvdXRwdXQvbG9ncy9LNzdKUC9QRmNsdXN0ZXJzLnBkZg0KICArIG91dHB1dC9sb2dzL0s3N0pQL1FzY29yZV9MMS5wZGYNCiAgKyBvdXRwdXQvbG9ncy9LNzdKUC9hdmdfaW50ZW5zaXR5LnBkZg0KKiBTYW1wbGUgcXVhbGl0eSBjaGVjaw0KICArIG91dHB1dC9sb2dzL0s3N0pQL0ZBU1RRQy9uZ3NSZXBvcnRzX0Zhc3RxYy5odG1sDQoqIEluZGV4LXN3aXRjaGluZw0KICArIG91dHB1dC9sb2dzL0s3N0pQL2luZGV4X3N3aXRjaGluZy5wZGYNCiogRmlsdGVyaW5nIHBsb3RzDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcHJlZmlsdF9xdWFscGxvdHMucGRmDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcG9zdGZpbHRfcXVhbHBsb3RzLnBkZg0KKiBEQURBMiBlcnJvciBtb2RlbA0KICArIG91dHB1dC9sb2dzL0s3N0pQL2ZjaWRfZXJyb3Jtb2RlbC5wZGYNCiogUmVhZHMgc3Vydml2aW5nIHBpcGVsaW5lDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcmVhZF9zdXJ2aXZhbC5wZGYgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+Tk9UIFdPUktJTkc8L3NwYW4+Lg0KDQoNCiMgQW5hbHlzZSB0aGUgcmVzdWx0cw0KDQpUaGUgb3V0cHV0cyBvZiB0aGUgcGlwZWxpbmUgYXJlIGFuIEFTViB0YWJsZSwgdGF4b25vbXkgdGFibGUsIGFuZCBzYW1wbGUgZGF0YSwgYXMgd2VsbCBhcyBhIHBoeWxvc2VxIG9iamVjdCBjb250YWluaW5nIHRoZXNlIDMgY29tcG9uZW50cy4gdGhlc2UgYXJlIGxvY2F0ZWQgaW4gdGhlIGZvbGxvd2luZyBkaXJlY3Rvcmllcw0KDQoqIG91dHB1dC9yZXN1bHRzL2ZpbmFsL3NlcXRhYi5jc3YNCiogb3V0cHV0L3Jlc3VsdHMvZmluYWwvdGF4dGFiLmNzdg0KKiBvdXRwdXQvcmVzdWx0cy9maW5hbC9zYW1kZi5jc3YNCiogb3V0cHV0L3Jkcy9wc19maWx0ZXJlZC5yZHMNCg0KDQojIyBSZWFkIGluIGRhdGENCg0KSGVyZSB3ZSByZWFkIGluIHRoZSBwaHlsb3NlcSBvYmplY3QgY29udGFpbmluZyB0aGUgQVNWIHRhYmxlLCB0YXhvbm9teSB0YWJsZSwgYW5kIHNhbXBsZSBkYXRhDQpgYGB7ciBwaHlsb3NlcSwgZXZhbD1UUlVFfQ0KcHMgPC0gcmVhZFJEUygib3V0cHV0L3Jkcy9wc19maWx0ZXJlZC5yZHMiKQ0KDQojIFR1cm4gdGhlIHBoeWxvc2VxIG9iamVjdCBpbnRvIGFuIFIgZGF0YSBmcmFtZSwgYW5kIGFwcGx5IDAuMDElIG1pbmltdW0gYWJ1bmRhbmNlIHRocmVzaG9sZA0Kc3VtbWFyeV9kYXQgPC0gcHMgJT4lDQogIHNwZWVkeXNlcTo6dGF4X2dsb20odGF4cmFuayA9ICJTcGVjaWVzIikgJT4lICMgTWVyZ2VzIGFsbCBBU1ZzIGFzc2lnbmVkIHRvIHRoZSBzYW1lIHNwZWNpZXMNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAgJT4lICMgVHJhbnNmb3JtcyB0byBkYXRhIGZyYW1lDQogIGZpbHRlcihBYnVuZGFuY2UgPiAwICkgICU+JQ0KICBkcGx5cjo6c2VsZWN0KE9UVSwgU2FtcGxlLCBBYnVuZGFuY2UscGNyX3ByaW1lcnMsIHNhbXBsZV9pZCwgc2FtcGxlX25hbWUsIGVudmlyb25tZW50LCBjb2xsZWN0aW9uX2xvY2F0aW9uLCBjb2xsZWN0aW9uX2RhdGUsIGZjaWQsIHJhbmtfbmFtZXMocHMpKSAlPiUNCiAgZ3JvdXBfYnkoc2FtcGxlX2lkLCBwY3JfcHJpbWVycykgJT4lDQogIG11dGF0ZV9hdCh2YXJzKEFidW5kYW5jZSksIH4gLiAvIHN1bSguKSApICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMWUtNCkgIyBSZW1vdmUgYWxsIHVuZGVyIDAuMDElIGFidW5kYW5jZQ0KDQpgYGANCg0KDQojIyBIZWF0bWFwcw0KYGBge3IgaGVhdG1hcCwgZXZhbD1UUlVFfQ0KZ2cuaGVhdG1hcCA8LSBzdW1tYXJ5X2RhdCU+JQ0KICBtdXRhdGUoc2FtcGxlX25hbWUgPSBmYWN0b3Ioc2FtcGxlX25hbWUsbGV2ZWxzPWMoIlRyYXAxIiwgIlRyYXA2IiwgIlRyYXA3IiwgIlRyYXAxOSIsICJUcmFwMjAiKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9c2FtcGxlX25hbWUsIHk9U3BlY2llcywgZmlsbD1BYnVuZGFuY2UpKSArDQogICAgZ2VvbV90aWxlKCkgKw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgbmEudmFsdWUgPSBOQSwgYWxwaGE9MC45KSArDQogICAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9cmV2KSsNCiAgICBiYXNlX3RoZW1lKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksDQogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSsNCiAgICAgIGxhYnMoeD0iU2FtcGxlIiwNCiAgICAgICAgICAgeT0iVGF4b24iLA0KICAgICAgICAgICBmaWxsPSJSZWxhdGl2ZSBhYnVuZGFuY2UiKSArDQogICAgZmFjZXRfZ3JpZCh+cGNyX3ByaW1lcnMsIGRyb3A9VFJVRSkNCg0KZ2dwbG90bHkoZ2cuaGVhdG1hcCkNCmBgYA0KDQojIyBCYXJwbG90cw0KYGBge3IgYmFycGxvdCwgZXZhbD1UUlVFfQ0KZ2cuYmFycGxvdCA8LSBzdW1tYXJ5X2RhdCU+JQ0KICBtdXRhdGUoc2FtcGxlX25hbWUgPSBmYWN0b3Ioc2FtcGxlX25hbWUsbGV2ZWxzPWMoIlRyYXAxIiwgIlRyYXA2IiwgIlRyYXA3IiwgIlRyYXAxOSIsICJUcmFwMjAiKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9c2FtcGxlX25hbWUsIHk9QWJ1bmRhbmNlLCBmaWxsPVNwZWNpZXMpKSArDQogICAgZ2VvbV9jb2woY29sb3VyPSJibGFjayIpICsNCiAgICAjc2NhbGVfZmlsbF92aXJpZGlzX2MobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBuYS52YWx1ZSA9IE5BLCBhbHBoYT0wLjkpICsNCiAgICAjc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9cmV2KSsNCiAgICBiYXNlX3RoZW1lKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJpdGFsaWMiKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpKw0KICAgICAgbGFicyh4PSJTYW1wbGUiLA0KICAgICAgICAgICB5PSJUYXhvbiIsDQogICAgICAgICAgIGZpbGw9IlJlbGF0aXZlIGFidW5kYW5jZSIpICsNCiAgICBmYWNldF9ncmlkKH5wY3JfcHJpbWVycywgZHJvcD1UUlVFKQ0KDQpnZ3Bsb3RseShnZy5iYXJwbG90KQ0KYGBg